A walkthrough of Ernest (2005)’s original analytical approach, from close reading of the paper.

Questions

  1. Is energy use across body size categories (regardless of species) uniform or multimodal?
  1. If energy use is not uniform across body size categories, does the species level body size distribution correspond to modes of energy use?

Data

Ernest data

Ernest drew data from the Andrews LTER, the Sevilleta, Niwot Ridge, and Portal.

The data available online do not quite match the descriptive statistics reported in Ernest (2005).

Translation to replicate-becs

Download raw data. By default data will be stored in subdirectories of replicate-becs/data/paper/raw/ for each site.

download_raw_paper_data()

Process raw data into the appropriate format. This is a data table with a record for each individual and columns for species and weight in grams. By default these tables will be stored in subdirectores of replicate-becs/data/paper/processed.

process_raw_data()
Loading in data version 1.106.0
[1] TRUE

Load data tables for each community. There should be 9 communities.

communities <- load_paper_data()
length(communities)
[1] 9

Each community should be a data table with columns for species and size for each individual, for example:

names(communities)
[1] "andrews"      "niwot"        "portal"       "sev-5pgrass"  "sev-5plarrea"
[6] "sev-goatdraw" "sev-rsgrass"  "sev-rslarrea" "sev-two22"   
head(communities[[1]])

Constructing distributions/metrics

Body size-energy use distributions (BSED)

Ernest method

  • Per individual, calculate metabolic rate as metabolic rate \(B \propto M^\frac{3}{4}\) where \(M\) is mass in grams.
  • Sum energy use of all individuals in body size classes of .2 natural log units.
  • Also try classes of .1 and .3 natural log units
  • Convert raw energy use values for each body size class into the proportion of all the energy used in that community used by that body size class. This allows for comparisons between communities.
Ernest 2005 Fig 1

Ernest 2005 Fig 1

Translation to replicate-becs

For every individual, calculate metabolic rate and assign to a size class.

communities_energy <- lapply(communities, FUN = make_community_table, ln_units = 0.2)
head(communities_energy[[1]])

For each community, sum total energy use for each size class, and convert to the proportion of total energy use for that community.

bseds <- lapply(communities_energy, FUN = make_bsed)
head(bseds[[1]])

Species-level body size distributions (BSD)

Ernest method

  • Frequency distributions of mean mass of each species in a community.
  • For plotting (but not statistics), smoothed using kernel density estimation.
  • Gaussian kernel to mimic the actual body size distribution in log space
  • avg. std dev of the mean of the logged masses = smoothing parameter \(h\)
  • align sampling points with the midpoint of each size class in the BSED
  • after Manly 1996, “Are there clumps in body-size distributions?”, Ecology

Translation to replicate-becs

Calculate mean mass of each species in each community.

bsds <- lapply(communities, FUN = make_bsd) 
head(bsds[[1]])

Energetic dominance (\(D_E\))

  • Define “energy use modes” as contiguous body size classes where the energy use of each size class > 5% of the community total.
  • i.e. a little bit more than the expectation if energy use is uniform across all body sizes
  • RMD is unsure of this. Doesn’t the uniform expectation depend on the number of size classes?
  • Calculate the total energy use for each species in the mode.
  • Calculate the “dominance” of the species with the highest energy use in that mode as \(D_E = p_{max}\), where \(p_{max}\) is the maximum proportion of energy use by any one species in a mode.
  • “a modification of the Berger-Parker dominance index (Berger and Parker 1970)”

Translation to replicate-becs

  • Find contiguous size classes where each class has >5% of total energy use
  • Calculate the total energy use for each species, and the proportion held by the species with the highest energy use (\(p_{max}\))
  • Return \(p_{max}\) for every mode, along with the min and max size classes in that mode for each community
energetic_dom <- lapply(communities_energy, FUN = energetic_dominance) 
head(energetic_dom[[1]])
  • To plot, combine all modes from all communities and plot a histogram of \(D_E\) values.

  • Out of curiousity, what happens if we define the modes with the cutoff proportional to the number of size classes (instead of a fixed 5%?)
energetic_dom_prop <- lapply(communities_energy, FUN = energetic_dominance, mode_cutoff = 'prop') 

  • RMD: They’re similar.

Statistical tests

Comparing BSEDs to uniform

Ernest approach

  • Use bootstrap sampling to compare to uniform distributions.
  • For every community, draw 10000 samples (sim communities):
  • Same number of individuals as the empirical community, drawn from a uniform distribution ranging from the smallest to largest body size individual metabolic rate of any individual in that community.
  • For sim communities and the empirical community, calculate a distribution overlap index (\(DOI\)):
  • \(DOI = \sum_k {|y_{ak} - y_{bk}|}\) where \(y\) is the value for size class \(k\) in communities \(a\) and \(b\).
  • \(DOI\) values will range from 0 (complete overlap) to 2 (no overlap).
  • For the BSED bootstraps, community \(a\) is the empirical or sim distribution, and community \(b\) is a true uniform distribution (i.e. \(y_{bk} = \frac{1}{\max(k)}\) for all \(k\))
  • “True uniform distribution”: There are exactly the same number of individuals of every size.
  • Calculate the \(DOI\) for all sim communities and the empirical.
  • Find the quantile value for the empirical \(DOI\) compared to the distribution of sim \(DOI\)s. This is the p-value; i.e. the proportion of sim uniform distributions with DOIs greater than the empirical.

Translation to replicate-becs

  • For a given empirical community, draw 10000 sim communities each with the same number of individuals \(n\), with body sizes randomly drawn from a uniform distribution from the minimum to maximum body size in that community.
  • Calculate the \(DOI\) of each sim community compared to a true uniform distribution.
  • True uniform distribution = every size from the minimum to the maximum size in the community (by .1g) has exactly one individual.
bsed_uniform_bootstraps <- lapply(communities, FUN = community_bootstrap,  bootstrap_function = 'bootstrap_unif_bsed_doi', nbootstraps = 10000)

See issue #4 on github.

Compare BSEDs among communities

Ernest approach

  • For every pair of communities, create a pool of masses of all individuals from both communities.
  • Draw two new communities with the same number of individuals as the empirical communities, pulling masses at random from the pool, with replacement.
  • Calculate the DOI for the BSEDs of the two sample communities.
  • Repeat 10000 for each pair.
  • The P value is the proportion of sample DOIs greater (i.e. less overlap) than the empirical value.

Translation to replicate-becs

  • For every pair of communities, pool all the masses
  • Resample two communities of the right sizes
  • Construct BSEDs for both communities
  • Calculate the DOI of the two BSEDs
  • Repeat 10000x
community_combination_indices = utils::combn(x = c(1:9), m = 2, simplify = TRUE) %>%
  t() %>%
  as.data.frame() %>%
  dplyr::rename(community_a = V1, community_b = V2)
combine_communities = function(indices, communities) {
  community_combination = list(community_a = communities[[indices[1]]], community_b = communities[[indices[2]]], community_names = c(names(communities)[[indices[1]]], names(communities)[[indices[2]]]))
  
  return(community_combination)
}
community_combinations = apply(community_combination_indices, MARGIN = 1, FUN = combine_communities, communities = communities)
bsed_crosscomm_bootstraps = lapply(community_combinations, FUN = community_bootstrap, 
                                   bootstrap_function = 'bootstrap_crosscomm_bseds', nbootstraps = 10000)

See histogram of p values for comparisons to see if commuities’ BSEDs are the same or different.

Testing BSDs for uniformity

Matching communities

Ernest (2005) refers to the communities with the site column above. To compare the communities above to the communities in the resurrected data set, we can try to match them based on the BSD and BSED plots (above) and species richness.

ernest_richness = read.csv(here::here("ernest-2005-files/ernest_richness.csv"), stringsAsFactors = F)
# Guesses based on body size plots
ernest_richness$community_name <- c('andrews', 'niwot', 'portal', 'sev-5pgrass', 
                                    'sev-rsgrass', 'sev-two22', 'sev-goatdraw', 
                                    'sev-5plarrea', 'sev-rslarrea')
bsds_richness = data.frame(community_name = names(bsds), stringsAsFactors = F)
bsds_richness$new_richness = NA
for(i in 1:nrow(bsds_richness)){
  bsds_richness$new_richness[i] = nrow(bsds[[i]])
}
# See if the richness values match
ernest_richness = ernest_richness %>%
  dplyr::left_join(bsds_richness, by = 'community_name') %>%
  dplyr::mutate(richness_match = (richness == new_richness))
print(ernest_richness)

We can get 2 (of 7) communities at the Sevilleta to match up, and Niwot and Andrews. The other pairings are as close as possible.

Moving forward, we can compare the results of the Kolmogorov-Smirnov tests based on the values in the appendix and these name pairings.

ernest_key = ernest_richness %>%
  dplyr::select(site, community_name)
write.csv(ernest_key, file = here::here('ernest-2005-files/ernest_key.csv'), row.names = F)

Ernest approach

  • \(\delta\)-corrected Kolmogorov-Smirnov test.
  • “The \(\delta\)-corrected K-S test increases the power of the test when sample sizes are small (n < 25; Zar 1999)”
  • The \(\delta\)-corrected test is not widely discussed online.
ernest_bsds_uniform_results = read.csv(here::here('ernest-2005-files/ernest_appendixA.csv'), stringsAsFactors = F) %>%
  dplyr::left_join(ernest_key, by = 'site')
print(ernest_bsds_uniform_results)

Translation to replicate-becs:

From Zar (1999) Biostatistical Analysis.

Base K-S test
  • Take vector of measurements \(X_i\).
  • For each \(X_i\) record the observed frequency \(f_i\) (number of observations with that value).
  • Determine cumulative observed frequencies \(F_i\) and cumulative relative frequencies \(\textrm{rel}F_i\):
  • \(\textrm{rel}F_i = \frac{F_i}{n}\) where \(n\) is the number of measurements taken.
  • \(\textrm{rel}F_i\) is the proportion of the sample that is measurements \(\leq X_i\).
  • For each \(X_i\), determine the cumulative relative expected frequency from the comparison distribution, \(\textrm{rel}\hat{F_i}\).
  • For a uniform distribution, \(\textrm{rel}\hat{F_i} = \frac{X_i - \min(X)}{\max(X) - \min(X)}\)
  • Determine \(D_i\) and \(D'_i\) as:
  • \(D_i = |{\textrm{rel}F_i - \textrm{rel}\hat{F_i}}|\)
  • \(D'_i = |{\textrm{rel}F_{i-1} - \textrm{rel}\hat{F_i}}|\)
  • note \(F_0 = 0\) so \(D'_1 = \textrm{rel}\hat{F_i}\)
  • The test statistic \(D\) is:
  • \(D = \max[(\max(D_i), (\max(D'_i)]\)
  • Compare to critical values from appendix.
\(\delta\)-corrected KS test
  • For small sample sizes (<25) we can obtain increased power using the \(\delta\)-corrected KS test.
  • For each \(i\) determine
  • \(\textrm{rel}G_i = \frac{F_i}{n + 1}\)
  • \(\textrm{rel}G'_i = \frac{F_i - 1}{n - 1}\)
  • Then obtain similar \(D\)s
  • \(D_{0, i} = |\textrm{rel}G_i - \textrm{rel}\hat{F_i}|\)
  • \(D_{1, i} = |\textrm{rel}G'_i - \textrm{rel}\hat{F_i}|\)
  • The test statistic is either \(\max(D_{0, i})\) or \(\max(D_{1, i})\), whichever leads to the highest level of significance/smallest probability. Look up significance in table from appendix. The 1 and 0 are the \(\delta\)s.

Tables of critical values were entered by hand from the appendix to Zar (1999).

The \(\delta\) corrected KS test does not correspond to the results from Ernest when the species mean body size values are on an untransformed scale.

Using the natural log of the species mean body size value, however…:

With mean mass logged, all the results replicate qualitatively (i.e. not significantly different from uniform) and Niwot, for which the currently-available data most closely matches that reported in Ernest (2005), replicates almost exactly numerically.

Comparing BSDs among communities

Ernest approach

Ernest (2005) used a two-sample Kolmogorov-Smirnov test to compare every possible combination of community-level BSDs.

Translation to replicate-becs

# use same community combinations as before
bsd_crosscomm_ks = lapply(community_combinations, FUN = ks_bsd, 
                          ln_mass_vals = F)

LS0tCnRpdGxlOiAiTmFycmF0aXZlIG9mIG9yaWdpbmFsIGFuYWx5c2lzIgphdXRob3I6ICJSZW5hdGEgRGlheiIKZGF0ZTogIjUvMTQvMjAxOSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGfQpsaWJyYXJ5KHJlcGxpY2F0ZWJlY3MpCmRvd25sb2FkX2RhdGEgPSBGQUxTRQpzb3VyY2Vfc2ltcyA9IFRSVUUKc2V0LnNlZWQoMzUyKQpgYGAKCkEgd2Fsa3Rocm91Z2ggb2YgRXJuZXN0ICgyMDA1KSdzIG9yaWdpbmFsIGFuYWx5dGljYWwgYXBwcm9hY2gsIGZyb20gY2xvc2UgcmVhZGluZyBvZiB0aGUgcGFwZXIuIAoKIyMgUXVlc3Rpb25zCgoxLiBJcyBlbmVyZ3kgdXNlIGFjcm9zcyBib2R5IHNpemUgY2F0ZWdvcmllcyAocmVnYXJkbGVzcyBvZiBzcGVjaWVzKSB1bmlmb3JtIG9yIG11bHRpbW9kYWw/Ci0gdW5pZm9ybSB3b3VsZCBjb3JyZXNwb25kIGdlbmVyYWxseSB0byBlbmVyZ2V0aWMgZXF1aXZhbGVuY2UvRGFtdXRoJ3MgcnVsZS4KLSBtdWx0aW1vZGFsIG1pZ2h0IHN1Z2dlc3QgZGlmZmVyZW50IHJlc291cmNlIGF2YWlsYWJpbGl0eSBmb3IgZGlmZmVyZW50IGJvZHkgc2l6ZXMuCjIuIElmIGVuZXJneSB1c2UgaXMgbm90IHVuaWZvcm0gYWNyb3NzIGJvZHkgc2l6ZSBjYXRlZ29yaWVzLCBkb2VzIHRoZSBzcGVjaWVzIGxldmVsIGJvZHkgc2l6ZSBkaXN0cmlidXRpb24gY29ycmVzcG9uZCB0byBtb2RlcyBvZiBlbmVyZ3kgdXNlPwotIGkuZS4gYXJlIHRoZXJlIG1vcmUgc3BlY2llcyB3aXRoIG1lYW4gYm9keSBzaXplcyBhcm91bmQgdGhlIG1vZGVzIG9mIHRoZSBib2R5IHNpemUtZW5lcmd5IHVzZSBkaXN0cmlidXRpb24/Ci0gaWYgc28sIG1heWJlIGl0J3MgZ29vZCB0byBiZSBjZXJ0YWluIHNpemVzLCBhbmQgc3BlY2llcyBhY2N1bXVsYXRlIGF0IHRob3NlIG9wdGltYS4KCgojIyBEYXRhCgojIyMjIEVybmVzdCBkYXRhCkVybmVzdCBkcmV3IGRhdGEgZnJvbSB0aGUgQW5kcmV3cyBMVEVSLCB0aGUgU2V2aWxsZXRhLCBOaXdvdCBSaWRnZSwgYW5kIFBvcnRhbC4gCgpUaGUgZGF0YSBhdmFpbGFibGUgb25saW5lIGRvIG5vdCBxdWl0ZSBtYXRjaCB0aGUgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyByZXBvcnRlZCBpbiBFcm5lc3QgKDIwMDUpLiAKCiMjIyMgVHJhbnNsYXRpb24gdG8gYHJlcGxpY2F0ZS1iZWNzYAoKRG93bmxvYWQgcmF3IGRhdGEuIEJ5IGRlZmF1bHQgZGF0YSB3aWxsIGJlIHN0b3JlZCBpbiBzdWJkaXJlY3RvcmllcyBvZiBgcmVwbGljYXRlLWJlY3MvZGF0YS9wYXBlci9yYXcvYCBmb3IgZWFjaCBzaXRlLiAKYGBge3IgZG93bmxvYWQgZGF0YSBpZiBub3QgZG93bmxvYWRlZCwgaW5jbHVkZSA9IEZ9CmlmKGRvd25sb2FkX2RhdGEpIGRvd25sb2FkX3Jhd19wYXBlcl9kYXRhKCkKYGBgCgpgYGB7ciBkb3dubG9hZCByYXcgZGF0YSwgZXZhbCA9IEZ9CmRvd25sb2FkX3Jhd19wYXBlcl9kYXRhKCkKYGBgCgpQcm9jZXNzIHJhdyBkYXRhIGludG8gdGhlIGFwcHJvcHJpYXRlIGZvcm1hdC4gVGhpcyBpcyBhIGRhdGEgdGFibGUgd2l0aCBhIHJlY29yZCBmb3IgZWFjaCBpbmRpdmlkdWFsIGFuZCBjb2x1bW5zIGZvciBgc3BlY2llc2AgYW5kIGB3ZWlnaHRgIGluIGdyYW1zLiBCeSBkZWZhdWx0IHRoZXNlIHRhYmxlcyB3aWxsIGJlIHN0b3JlZCBpbiBzdWJkaXJlY3RvcmVzIG9mIGByZXBsaWNhdGUtYmVjcy9kYXRhL3BhcGVyL3Byb2Nlc3NlZGAuIAoKYGBge3IgcHJvY2VzcyBwYXBlciBkYXRhfQpwcm9jZXNzX3Jhd19kYXRhKCkKYGBgCgpMb2FkIGRhdGEgdGFibGVzIGZvciBlYWNoIGNvbW11bml0eS4gVGhlcmUgc2hvdWxkIGJlIDkgY29tbXVuaXRpZXMuCgpgYGB7ciBsb2FkIGNvbW11bml0eSBkYXRhLCBlY2hvPVRSVUV9CmNvbW11bml0aWVzIDwtIGxvYWRfcGFwZXJfZGF0YSgpCgpsZW5ndGgoY29tbXVuaXRpZXMpCmBgYAoKRWFjaCBjb21tdW5pdHkgc2hvdWxkIGJlIGEgZGF0YSB0YWJsZSB3aXRoIGNvbHVtbnMgZm9yIHNwZWNpZXMgYW5kIHNpemUgZm9yIGVhY2ggaW5kaXZpZHVhbCwgZm9yIGV4YW1wbGU6CgpgYGB7ciBpbnNwZWN0IGNvbW11bml0eSBkYXRhfQpuYW1lcyhjb21tdW5pdGllcykKaGVhZChjb21tdW5pdGllc1tbMV1dKQpgYGAKCgojIyBDb25zdHJ1Y3RpbmcgZGlzdHJpYnV0aW9ucy9tZXRyaWNzCgojIyMgQm9keSBzaXplLWVuZXJneSB1c2UgZGlzdHJpYnV0aW9ucyAoQlNFRCkKCiMjIyMgRXJuZXN0IG1ldGhvZAoKLSBQZXIgaW5kaXZpZHVhbCwgY2FsY3VsYXRlIG1ldGFib2xpYyByYXRlIGFzIG1ldGFib2xpYyByYXRlICRCIFxwcm9wdG8gTV5cZnJhY3szfXs0fSQgd2hlcmUgJE0kIGlzIG1hc3MgaW4gZ3JhbXMuCi0gU3VtIGVuZXJneSB1c2Ugb2YgYWxsIGluZGl2aWR1YWxzIGluIGJvZHkgc2l6ZSBjbGFzc2VzIG9mIC4yIG5hdHVyYWwgbG9nIHVuaXRzLgotIEFsc28gdHJ5IGNsYXNzZXMgb2YgLjEgYW5kIC4zIG5hdHVyYWwgbG9nIHVuaXRzCi0gQ29udmVydCByYXcgZW5lcmd5IHVzZSB2YWx1ZXMgZm9yIGVhY2ggYm9keSBzaXplIGNsYXNzIGludG8gdGhlIHByb3BvcnRpb24gb2YgYWxsIHRoZSBlbmVyZ3kgdXNlZCBpbiB0aGF0IGNvbW11bml0eSB1c2VkIGJ5IHRoYXQgYm9keSBzaXplIGNsYXNzLiBUaGlzIGFsbG93cyBmb3IgY29tcGFyaXNvbnMgYmV0d2VlbiBjb21tdW5pdGllcy4KCgohW0VybmVzdCAyMDA1IEZpZyAxXSguLi9lcm5lc3QtMjAwNS1maWxlcy9lcm5lc3QyMDA1X2ZpZzEucG5nKQoKCiMjIyMgVHJhbnNsYXRpb24gdG8gYHJlcGxpY2F0ZS1iZWNzYAoKRm9yIGV2ZXJ5IGluZGl2aWR1YWwsIGNhbGN1bGF0ZSBtZXRhYm9saWMgcmF0ZSBhbmQgYXNzaWduIHRvIGEgc2l6ZSBjbGFzcy4gCgpgYGB7ciBjb25zdHJ1Y3QgQlNFRHN9CmNvbW11bml0aWVzX2VuZXJneSA8LSBsYXBwbHkoY29tbXVuaXRpZXMsIEZVTiA9IG1ha2VfY29tbXVuaXR5X3RhYmxlLCBsbl91bml0cyA9IDAuMikKCmhlYWQoY29tbXVuaXRpZXNfZW5lcmd5W1sxXV0pCmBgYAoKRm9yIGVhY2ggY29tbXVuaXR5LCBzdW0gdG90YWwgZW5lcmd5IHVzZSBmb3IgZWFjaCBzaXplIGNsYXNzLCBhbmQgY29udmVydCB0byB0aGUgcHJvcG9ydGlvbiBvZiB0b3RhbCBlbmVyZ3kgdXNlIGZvciB0aGF0IGNvbW11bml0eS4KCmBgYHtyIG1ha2UgYnNlZHN9CmJzZWRzIDwtIGxhcHBseShjb21tdW5pdGllc19lbmVyZ3ksIEZVTiA9IG1ha2VfYnNlZCkKCmhlYWQoYnNlZHNbWzFdXSkKYGBgCgpgYGB7ciBwbG90IGJzZWRzLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9Cgpic2Vkc19wbG90IDwtIHBsb3RfcGFwZXJfZGlzdHMoYnNlZHMsIGRpc3RfdHlwZSA9ICdic2VkJykKCmludmlzaWJsZShic2Vkc19wbG90KQpgYGAKCiMjIyBTcGVjaWVzLWxldmVsIGJvZHkgc2l6ZSBkaXN0cmlidXRpb25zIChCU0QpCgojIyMjIEVybmVzdCBtZXRob2QKLSBGcmVxdWVuY3kgZGlzdHJpYnV0aW9ucyBvZiBtZWFuIG1hc3Mgb2YgZWFjaCBzcGVjaWVzIGluIGEgY29tbXVuaXR5LgotIEZvciBwbG90dGluZyAoYnV0IG5vdCBzdGF0aXN0aWNzKSwgc21vb3RoZWQgdXNpbmcga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbi4gCi0gR2F1c3NpYW4ga2VybmVsIHRvIG1pbWljIHRoZSBhY3R1YWwgYm9keSBzaXplIGRpc3RyaWJ1dGlvbiBpbiBsb2cgc3BhY2UKLSBhdmcuIHN0ZCBkZXYgb2YgdGhlIG1lYW4gb2YgdGhlIGxvZ2dlZCBtYXNzZXMgPSBzbW9vdGhpbmcgcGFyYW1ldGVyICRoJAotIGFsaWduIHNhbXBsaW5nIHBvaW50cyB3aXRoIHRoZSBtaWRwb2ludCBvZiBlYWNoIHNpemUgY2xhc3MgaW4gdGhlIEJTRUQKLSBhZnRlciBNYW5seSAxOTk2LCAiQXJlIHRoZXJlIGNsdW1wcyBpbiBib2R5LXNpemUgZGlzdHJpYnV0aW9ucz8iLCBfRWNvbG9neV8KCiMjIyMgVHJhbnNsYXRpb24gdG8gYHJlcGxpY2F0ZS1iZWNzYAoKQ2FsY3VsYXRlIG1lYW4gbWFzcyBvZiBlYWNoIHNwZWNpZXMgaW4gZWFjaCBjb21tdW5pdHkuIAoKYGBge3IgY29uc3RydWN0IGJzZHN9IAoKYnNkcyA8LSBsYXBwbHkoY29tbXVuaXRpZXMsIEZVTiA9IG1ha2VfYnNkKSAKCmhlYWQoYnNkc1tbMV1dKQpgYGAKCmBgYHtyIHBsb3QgYnNkcywgZWNobz1GQUxTRSwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQoKYnNkc19wbG90IDwtIHBsb3RfcGFwZXJfZGlzdHMoYnNkcywgZGlzdF90eXBlID0gJ2JzZCcpCgppbnZpc2libGUoYnNkc19wbG90KQpgYGAKCgojIyMgRW5lcmdldGljIGRvbWluYW5jZSAoJERfRSQpCgotIERlZmluZSAiZW5lcmd5IHVzZSBtb2RlcyIgYXMgY29udGlndW91cyBib2R5IHNpemUgY2xhc3NlcyB3aGVyZSB0aGUgZW5lcmd5IHVzZSBvZiBlYWNoIHNpemUgY2xhc3MgPiA1JSBvZiB0aGUgY29tbXVuaXR5IHRvdGFsLiAKLSBpLmUuIGEgbGl0dGxlIGJpdCBtb3JlIHRoYW4gdGhlIGV4cGVjdGF0aW9uIGlmIGVuZXJneSB1c2UgaXMgdW5pZm9ybSBhY3Jvc3MgYWxsIGJvZHkgc2l6ZXMKLSBSTUQgaXMgdW5zdXJlIG9mIHRoaXMuIERvZXNuJ3QgdGhlIHVuaWZvcm0gZXhwZWN0YXRpb24gZGVwZW5kIG9uIHRoZSBudW1iZXIgb2Ygc2l6ZSBjbGFzc2VzPwotIENhbGN1bGF0ZSB0aGUgdG90YWwgZW5lcmd5IHVzZSBmb3IgZWFjaCBzcGVjaWVzIGluIHRoZSBtb2RlLiAKLSBDYWxjdWxhdGUgdGhlICJkb21pbmFuY2UiIG9mIHRoZSBzcGVjaWVzIHdpdGggdGhlIGhpZ2hlc3QgZW5lcmd5IHVzZSBpbiB0aGF0IG1vZGUgYXMgJERfRSA9IHBfe21heH0kLCB3aGVyZSAkcF97bWF4fSQgaXMgdGhlIG1heGltdW0gcHJvcG9ydGlvbiBvZiBlbmVyZ3kgdXNlIGJ5IGFueSBvbmUgc3BlY2llcyBpbiBhIG1vZGUuIAotICJhIG1vZGlmaWNhdGlvbiBvZiB0aGUgQmVyZ2VyLVBhcmtlciBkb21pbmFuY2UgaW5kZXggKEJlcmdlciBhbmQgUGFya2VyIDE5NzApIgoKIyMjIyBUcmFuc2xhdGlvbiB0byBgcmVwbGljYXRlLWJlY3NgCgotIEZpbmQgY29udGlndW91cyBzaXplIGNsYXNzZXMgd2hlcmUgZWFjaCBjbGFzcyBoYXMgPjUlIG9mIHRvdGFsIGVuZXJneSB1c2UKLSBDYWxjdWxhdGUgdGhlIHRvdGFsIGVuZXJneSB1c2UgZm9yIGVhY2ggc3BlY2llcywgYW5kIHRoZSBwcm9wb3J0aW9uIGhlbGQgYnkgdGhlIHNwZWNpZXMgd2l0aCB0aGUgaGlnaGVzdCBlbmVyZ3kgdXNlICgkcF97bWF4fSQpCi0gUmV0dXJuICRwX3ttYXh9JCBmb3IgZXZlcnkgbW9kZSwgYWxvbmcgd2l0aCB0aGUgbWluIGFuZCBtYXggc2l6ZSBjbGFzc2VzIGluIHRoYXQgbW9kZSBmb3IgZWFjaCBjb21tdW5pdHkKCmBgYHtyIGVuZXJnZXRpYyBkb21pbmFuY2V9CgplbmVyZ2V0aWNfZG9tIDwtIGxhcHBseShjb21tdW5pdGllc19lbmVyZ3ksIEZVTiA9IGVuZXJnZXRpY19kb21pbmFuY2UpIAoKaGVhZChlbmVyZ2V0aWNfZG9tW1sxXV0pCgpgYGAKCi0gVG8gcGxvdCwgY29tYmluZSBhbGwgbW9kZXMgZnJvbSBhbGwgY29tbXVuaXRpZXMgYW5kIHBsb3QgYSBoaXN0b2dyYW0gb2YgJERfRSQgdmFsdWVzLgoKYGBge3IgcGxvdCBFZCwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NX0KZV9kb21fcGxvdCA8LSBwbG90X2VfZG9tKGVuZXJnZXRpY19kb20pCmVfZG9tX3Bsb3QKYGBgCgoKLSBPdXQgb2YgY3VyaW91c2l0eSwgd2hhdCBoYXBwZW5zIGlmIHdlIGRlZmluZSB0aGUgbW9kZXMgd2l0aCB0aGUgY3V0b2ZmIHByb3BvcnRpb25hbCB0byB0aGUgbnVtYmVyIG9mIHNpemUgY2xhc3NlcyAoaW5zdGVhZCBvZiBhIGZpeGVkIDUlPykKCmBgYHtyIGVkb20gcHJvcG9ydGlvbmFsfQplbmVyZ2V0aWNfZG9tX3Byb3AgPC0gbGFwcGx5KGNvbW11bml0aWVzX2VuZXJneSwgRlVOID0gZW5lcmdldGljX2RvbWluYW5jZSwgbW9kZV9jdXRvZmYgPSAncHJvcCcpIApgYGAKCmBgYHtyIHBsb3QgZWRvbSBwcm9wb3J0aW9uYWwsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTV9CmVfZG9tX3Byb3BfcGxvdCA8LSBwbG90X2VfZG9tKGVuZXJnZXRpY19kb21fcHJvcCkKZV9kb21fcHJvcF9wbG90CmBgYAoKLSBSTUQ6IFRoZXkncmUgc2ltaWxhci4gCgojIyBTdGF0aXN0aWNhbCB0ZXN0cwoKIyMjIENvbXBhcmluZyBCU0VEcyB0byB1bmlmb3JtCgojIyMjIEVybmVzdCBhcHByb2FjaAoKLSBVc2UgYm9vdHN0cmFwIHNhbXBsaW5nIHRvIGNvbXBhcmUgdG8gdW5pZm9ybSBkaXN0cmlidXRpb25zLgotIEZvciBldmVyeSBjb21tdW5pdHksIGRyYXcgMTAwMDAgc2FtcGxlcyAoc2ltIGNvbW11bml0aWVzKToKLSBTYW1lIG51bWJlciBvZiBpbmRpdmlkdWFscyBhcyB0aGUgZW1waXJpY2FsIGNvbW11bml0eSwgZHJhd24gZnJvbSBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uIHJhbmdpbmcgZnJvbSB0aGUgc21hbGxlc3QgdG8gbGFyZ2VzdCB+fmJvZHkgc2l6ZX5+IGluZGl2aWR1YWwgbWV0YWJvbGljIHJhdGUgb2YgYW55IGluZGl2aWR1YWwgaW4gdGhhdCBjb21tdW5pdHkuCi0gRm9yIHNpbSBjb21tdW5pdGllcyBhbmQgdGhlIGVtcGlyaWNhbCBjb21tdW5pdHksIGNhbGN1bGF0ZSBhIGRpc3RyaWJ1dGlvbiBvdmVybGFwIGluZGV4ICgkRE9JJCk6Ci0gJERPSSA9IFxzdW1fayB7fHlfe2FrfSAtIHlfe2JrfXx9JCB3aGVyZSAkeSQgaXMgdGhlIHZhbHVlIGZvciBzaXplIGNsYXNzICRrJCBpbiBjb21tdW5pdGllcyAkYSQgYW5kICRiJC4KLSAkRE9JJCB2YWx1ZXMgd2lsbCByYW5nZSBmcm9tIDAgKGNvbXBsZXRlIG92ZXJsYXApIHRvIDIgKG5vIG92ZXJsYXApLiAKLSBGb3IgdGhlIEJTRUQgYm9vdHN0cmFwcywgY29tbXVuaXR5ICRhJCBpcyB0aGUgZW1waXJpY2FsIG9yIHNpbSBkaXN0cmlidXRpb24sIGFuZCBjb21tdW5pdHkgJGIkIGlzIGEgdHJ1ZSB1bmlmb3JtIGRpc3RyaWJ1dGlvbiB+fihpLmUuICR5X3tia30gPSBcZnJhY3sxfXtcbWF4KGspfSQgZm9yIGFsbCAkayQpfn4KLSAiVHJ1ZSB1bmlmb3JtIGRpc3RyaWJ1dGlvbiI6IFRoZXJlIGFyZSBleGFjdGx5IHRoZSBzYW1lIG51bWJlciBvZiBpbmRpdmlkdWFscyBvZiBldmVyeSBzaXplLiAKLSBDYWxjdWxhdGUgdGhlICRET0kkIGZvciBhbGwgc2ltIGNvbW11bml0aWVzIGFuZCB0aGUgZW1waXJpY2FsLgotIEZpbmQgdGhlIHF1YW50aWxlIHZhbHVlIGZvciB0aGUgZW1waXJpY2FsICRET0kkIGNvbXBhcmVkIHRvIHRoZSBkaXN0cmlidXRpb24gb2Ygc2ltICRET0kkcy4gVGhpcyBpcyB0aGUgcC12YWx1ZTsgaS5lLiB0aGUgcHJvcG9ydGlvbiBvZiBzaW0gdW5pZm9ybSBkaXN0cmlidXRpb25zIHdpdGggRE9JcyBncmVhdGVyIHRoYW4gdGhlIGVtcGlyaWNhbC4KCiMjIyMgVHJhbnNsYXRpb24gdG8gYHJlcGxpY2F0ZS1iZWNzYAoKLSBGb3IgYSBnaXZlbiBlbXBpcmljYWwgY29tbXVuaXR5LCBkcmF3IDEwMDAwIHNpbSBjb21tdW5pdGllcyBlYWNoIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIGluZGl2aWR1YWxzICRuJCwgd2l0aCBib2R5IHNpemVzIHJhbmRvbWx5IGRyYXduIGZyb20gYSB1bmlmb3JtIGRpc3RyaWJ1dGlvbiBmcm9tIHRoZSBtaW5pbXVtIHRvIG1heGltdW0gYm9keSBzaXplIGluIHRoYXQgY29tbXVuaXR5LgotIENhbGN1bGF0ZSB0aGUgJERPSSQgb2YgZWFjaCBzaW0gY29tbXVuaXR5IGNvbXBhcmVkIHRvIGEgdHJ1ZSB1bmlmb3JtIGRpc3RyaWJ1dGlvbi4gCi0gVHJ1ZSB1bmlmb3JtIGRpc3RyaWJ1dGlvbiA9IGV2ZXJ5IHNpemUgZnJvbSB0aGUgbWluaW11bSB0byB0aGUgbWF4aW11bSBzaXplIGluIHRoZSBjb21tdW5pdHkgKGJ5IC4xZykgaGFzIGV4YWN0bHkgb25lIGluZGl2aWR1YWwuCgpgYGB7ciBCU0VELXVuaWZvcm0gYm9vdHN0cmFwcGVkIERPSXMsIGV2YWwgPSBGfQoKYnNlZF91bmlmb3JtX2Jvb3RzdHJhcHMgPC0gbGFwcGx5KGNvbW11bml0aWVzLCBGVU4gPSBjb21tdW5pdHlfYm9vdHN0cmFwLCAgYm9vdHN0cmFwX2Z1bmN0aW9uID0gJ2Jvb3RzdHJhcF91bmlmX2JzZWRfZG9pJywgbmJvb3RzdHJhcHMgPSAxMDAwMCkKCmBgYAoKYGBge3Igc291cmNlIG9yIGRyYXcgQlNFRCB1bmlmb3JtIGJvb3RzdHJhcHMsIGluY2x1ZGUgPSBGfQppZihzb3VyY2Vfc2ltcykgewogIGxvYWQoaGVyZTo6aGVyZSgnZGF0YScsICdzaW1zJywgJ2JzZWRfdW5pZm9ybV9ib290c3RyYXBzLlJkYXRhJykpCn0gZWxzZSB7CiAgCmJzZWRfdW5pZm9ybV9ib290c3RyYXBzIDwtIGxhcHBseShjb21tdW5pdGllcywgRlVOID0gY29tbXVuaXR5X2Jvb3RzdHJhcCwgIGJvb3RzdHJhcF9mdW5jdGlvbiA9ICdib290c3RyYXBfdW5pZl9ic2VkX2RvaScsIG5ib290c3RyYXBzID0gMTAwMDApCgpzYXZlKGJzZWRfdW5pZm9ybV9ib290c3RyYXBzLCBmaWxlID0gKGhlcmU6OmhlcmUoJ2RhdGEnLCAnc2ltcycsICdic2VkX3VuaWZvcm1fYm9vdHN0cmFwcy5SZGF0YScpKSkKfQpgYGAKCgpfU2VlIGlzc3VlICM0IG9uIGdpdGh1Yi5fCgoKYGBge3IgcGxvdCBCU0VELXVuaWZvcm0gYm9vdHN0cmFwIERPSXMgdiBlbXBpcmljYWwsZWNobz1GQUxTRSwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfSAKCmJzZWRfdW5pZm9ybV9ib290c3RyYXBfcGxvdCA8LSBwbG90X3BhcGVyX2Rpc3RzKGJzZWRfdW5pZm9ybV9ib290c3RyYXBzLCBkaXN0X3R5cGUgPSAnYnNlZF9ib290c3RyYXBzJykKCmludmlzaWJsZShic2VkX3VuaWZvcm1fYm9vdHN0cmFwX3Bsb3QpCmBgYAoKIyMjIENvbXBhcmUgQlNFRHMgYW1vbmcgY29tbXVuaXRpZXMKCiMjIyMgRXJuZXN0IGFwcHJvYWNoCi0gRm9yIGV2ZXJ5IHBhaXIgb2YgY29tbXVuaXRpZXMsIGNyZWF0ZSBhIHBvb2wgb2YgbWFzc2VzIG9mIGFsbCBpbmRpdmlkdWFscyBmcm9tIGJvdGggY29tbXVuaXRpZXMuCi0gRHJhdyB0d28gbmV3IGNvbW11bml0aWVzIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIGluZGl2aWR1YWxzIGFzIHRoZSBlbXBpcmljYWwgY29tbXVuaXRpZXMsIHB1bGxpbmcgbWFzc2VzIGF0IHJhbmRvbSBmcm9tIHRoZSBwb29sLCB3aXRoIHJlcGxhY2VtZW50LgotIENhbGN1bGF0ZSB0aGUgRE9JIGZvciB0aGUgQlNFRHMgb2YgdGhlIHR3byBzYW1wbGUgY29tbXVuaXRpZXMuCi0gUmVwZWF0IDEwMDAwIGZvciBlYWNoIHBhaXIuCi0gVGhlIFAgdmFsdWUgaXMgdGhlIHByb3BvcnRpb24gb2Ygc2FtcGxlIERPSXMgZ3JlYXRlciAoaS5lLiBsZXNzIG92ZXJsYXApIHRoYW4gdGhlIGVtcGlyaWNhbCB2YWx1ZS4gCgojIyMjIFRyYW5zbGF0aW9uIHRvIGByZXBsaWNhdGUtYmVjc2AKLSBGb3IgZXZlcnkgcGFpciBvZiBjb21tdW5pdGllcywgcG9vbCBhbGwgdGhlIG1hc3NlcwotIFJlc2FtcGxlIHR3byBjb21tdW5pdGllcyBvZiB0aGUgcmlnaHQgc2l6ZXMKLSBDb25zdHJ1Y3QgQlNFRHMgZm9yIGJvdGggY29tbXVuaXRpZXMKLSBDYWxjdWxhdGUgdGhlIERPSSBvZiB0aGUgdHdvIEJTRURzCi0gUmVwZWF0IDEwMDAweAoKYGBge3IgcGFpcnMgZm9yIGNyb3NzY29tbXVuaXR5IEJTRUQgY29tcGFyaXNvbnN9CmNvbW11bml0eV9jb21iaW5hdGlvbl9pbmRpY2VzID0gdXRpbHM6OmNvbWJuKHggPSBjKDE6OSksIG0gPSAyLCBzaW1wbGlmeSA9IFRSVUUpICU+JQogIHQoKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgZHBseXI6OnJlbmFtZShjb21tdW5pdHlfYSA9IFYxLCBjb21tdW5pdHlfYiA9IFYyKQoKY29tYmluZV9jb21tdW5pdGllcyA9IGZ1bmN0aW9uKGluZGljZXMsIGNvbW11bml0aWVzKSB7CiAgY29tbXVuaXR5X2NvbWJpbmF0aW9uID0gbGlzdChjb21tdW5pdHlfYSA9IGNvbW11bml0aWVzW1tpbmRpY2VzWzFdXV0sIGNvbW11bml0eV9iID0gY29tbXVuaXRpZXNbW2luZGljZXNbMl1dXSwgY29tbXVuaXR5X25hbWVzID0gYyhuYW1lcyhjb21tdW5pdGllcylbW2luZGljZXNbMV1dXSwgbmFtZXMoY29tbXVuaXRpZXMpW1tpbmRpY2VzWzJdXV0pKQogIAogIHJldHVybihjb21tdW5pdHlfY29tYmluYXRpb24pCn0KCmNvbW11bml0eV9jb21iaW5hdGlvbnMgPSBhcHBseShjb21tdW5pdHlfY29tYmluYXRpb25faW5kaWNlcywgTUFSR0lOID0gMSwgRlVOID0gY29tYmluZV9jb21tdW5pdGllcywgY29tbXVuaXRpZXMgPSBjb21tdW5pdGllcykKCmBgYAoKYGBge3IgY3Jvc3MgY29tbXVuaXR5IEJTRUQgY29tcGFyaXNvbnMsIGV2YWwgPUZ9Cgpic2VkX2Nyb3NzY29tbV9ib290c3RyYXBzID0gbGFwcGx5KGNvbW11bml0eV9jb21iaW5hdGlvbnMsIEZVTiA9IGNvbW11bml0eV9ib290c3RyYXAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvb3RzdHJhcF9mdW5jdGlvbiA9ICdib290c3RyYXBfY3Jvc3Njb21tX2JzZWRzJywgbmJvb3RzdHJhcHMgPSAxMDAwMCkKCgpgYGAKCgpgYGB7ciBzb3VyY2Ugb3IgZHJhdyBjcm9zcyBjb21tdW5pdHkgQlNFRCwgaW5jbHVkZSA9IEZ9CmlmKHNvdXJjZV9zaW1zKSB7CiAgbG9hZChoZXJlOjpoZXJlKCdkYXRhJywgJ3NpbXMnLCAnYnNlZF9jcm9zc2NvbW1fYm9vdHN0cmFwcy5SZGF0YScpKQp9IGVsc2UgewogIApic2VkX2Nyb3NzY29tbV9ib290c3RyYXBzID0gbGFwcGx5KGNvbW11bml0eV9jb21iaW5hdGlvbnMsIEZVTiA9IGNvbW11bml0eV9ib290c3RyYXAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvb3RzdHJhcF9mdW5jdGlvbiA9ICdib290c3RyYXBfY3Jvc3Njb21tX2JzZWRzJywgbmJvb3RzdHJhcHMgPSAxMDAwMCkKCnNhdmUoYnNlZF9jcm9zc2NvbW1fYm9vdHN0cmFwcywgZmlsZSA9IChoZXJlOjpoZXJlKCdkYXRhJywgJ3NpbXMnLCAnYnNlZF9jcm9zc2NvbW1fYm9vdHN0cmFwcy5SZGF0YScpKSkKfQpgYGAKCmBgYHtyIHBsb3QgY3Jvc3MgY29tbXVuaXR5IGNvbXBhcmlzb25zLCBlY2hvID0gRiwgZmlnLmhlaWdodD0zMCwgZmlnLndpZHRoPTEwfQoKY3Jvc3Njb21tX2Jvb3RzdHJhcF9wbG90ID0gcGxvdF9jcm9zc2NvbW1fYnNlZHMoYnNlZF9jcm9zc2NvbW1fYm9vdHN0cmFwcykKCmludmlzaWJsZShjcm9zc2NvbW1fYm9vdHN0cmFwX3Bsb3QpCgpgYGAKCmBgYHtyIHBsb3QgY3Jvc3MgY29tbXVuaXR5IHAgdmFsdWVzLCBlY2hvID0gRiwgZmlnLmhlaWdodCA9IDUsIGZpZy53aWR0aCA9IDV9CnB2YWxzX2hpc3RvZ3JhbSA9IHBsb3RfYm9vdHN0cmFwX3B2YWxzKGJzZWRfY3Jvc3Njb21tX2Jvb3RzdHJhcHMpCgpwdmFsc19oaXN0b2dyYW0KYGBgCgoKU2VlIGhpc3RvZ3JhbSBvZiBwIHZhbHVlcyBmb3IgY29tcGFyaXNvbnMgdG8gc2VlIGlmIGNvbW11aXRpZXMnIEJTRURzIGFyZSB0aGUgc2FtZSBvciBkaWZmZXJlbnQuCgojIyMgVGVzdGluZyBCU0RzIGZvciB1bmlmb3JtaXR5CgojIyMjIE1hdGNoaW5nIGNvbW11bml0aWVzCgpFcm5lc3QgKDIwMDUpIHJlZmVycyB0byB0aGUgY29tbXVuaXRpZXMgd2l0aCB0aGUgYHNpdGVgIGNvbHVtbiBhYm92ZS4gVG8gY29tcGFyZSB0aGUgY29tbXVuaXRpZXMgYWJvdmUgdG8gdGhlIGNvbW11bml0aWVzIGluIHRoZSByZXN1cnJlY3RlZCBkYXRhIHNldCwgd2UgY2FuIHRyeSB0byBtYXRjaCB0aGVtIGJhc2VkIG9uIHRoZSBCU0QgYW5kIEJTRUQgcGxvdHMgKGFib3ZlKSBhbmQgc3BlY2llcyByaWNobmVzcy4gCgpgYGB7ciBtYXRjaCBic2RzfQplcm5lc3RfcmljaG5lc3MgPSByZWFkLmNzdihoZXJlOjpoZXJlKCJlcm5lc3QtMjAwNS1maWxlcy9lcm5lc3RfcmljaG5lc3MuY3N2IiksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyBHdWVzc2VzIGJhc2VkIG9uIGJvZHkgc2l6ZSBwbG90cwplcm5lc3RfcmljaG5lc3MkY29tbXVuaXR5X25hbWUgPC0gYygnYW5kcmV3cycsICduaXdvdCcsICdwb3J0YWwnLCAnc2V2LTVwZ3Jhc3MnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3Nldi1yc2dyYXNzJywgJ3Nldi10d28yMicsICdzZXYtZ29hdGRyYXcnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3Nldi01cGxhcnJlYScsICdzZXYtcnNsYXJyZWEnKQoKCmJzZHNfcmljaG5lc3MgPSBkYXRhLmZyYW1lKGNvbW11bml0eV9uYW1lID0gbmFtZXMoYnNkcyksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpic2RzX3JpY2huZXNzJG5ld19yaWNobmVzcyA9IE5BCmZvcihpIGluIDE6bnJvdyhic2RzX3JpY2huZXNzKSl7CiAgYnNkc19yaWNobmVzcyRuZXdfcmljaG5lc3NbaV0gPSBucm93KGJzZHNbW2ldXSkKfQoKIyBTZWUgaWYgdGhlIHJpY2huZXNzIHZhbHVlcyBtYXRjaAplcm5lc3RfcmljaG5lc3MgPSBlcm5lc3RfcmljaG5lc3MgJT4lCiAgZHBseXI6OmxlZnRfam9pbihic2RzX3JpY2huZXNzLCBieSA9ICdjb21tdW5pdHlfbmFtZScpICU+JQogIGRwbHlyOjptdXRhdGUocmljaG5lc3NfbWF0Y2ggPSAocmljaG5lc3MgPT0gbmV3X3JpY2huZXNzKSkKCnByaW50KGVybmVzdF9yaWNobmVzcykKCgpgYGAKCldlIGNhbiBnZXQgMiAob2YgNykgY29tbXVuaXRpZXMgYXQgdGhlIFNldmlsbGV0YSB0byBtYXRjaCB1cCwgYW5kIE5pd290IGFuZCBBbmRyZXdzLiBUaGUgb3RoZXIgcGFpcmluZ3MgYXJlIGFzIGNsb3NlIGFzIHBvc3NpYmxlLgoKTW92aW5nIGZvcndhcmQsIHdlIGNhbiBjb21wYXJlIHRoZSByZXN1bHRzIG9mIHRoZSBLb2xtb2dvcm92LVNtaXJub3YgdGVzdHMgYmFzZWQgb24gdGhlIHZhbHVlcyBpbiB0aGUgYXBwZW5kaXggYW5kIHRoZXNlIG5hbWUgcGFpcmluZ3MuCgpgYGB7ciBlcm5lc3Qga2V5fQoKZXJuZXN0X2tleSA9IGVybmVzdF9yaWNobmVzcyAlPiUKICBkcGx5cjo6c2VsZWN0KHNpdGUsIGNvbW11bml0eV9uYW1lKQoKd3JpdGUuY3N2KGVybmVzdF9rZXksIGZpbGUgPSBoZXJlOjpoZXJlKCdlcm5lc3QtMjAwNS1maWxlcy9lcm5lc3Rfa2V5LmNzdicpLCByb3cubmFtZXMgPSBGKQpgYGAKCiMjIyMgRXJuZXN0IGFwcHJvYWNoCi0gJFxkZWx0YSQtY29ycmVjdGVkIEtvbG1vZ29yb3YtU21pcm5vdiB0ZXN0LiAKLSAiVGhlICRcZGVsdGEkLWNvcnJlY3RlZCBLLVMgdGVzdCBpbmNyZWFzZXMgdGhlIHBvd2VyIG9mIHRoZSB0ZXN0IHdoZW4gc2FtcGxlIHNpemVzIGFyZSBzbWFsbCAobiA8IDI1OyBaYXIgMTk5OSkiCi0gVGhlICRcZGVsdGEkLWNvcnJlY3RlZCB0ZXN0IGlzIG5vdCB3aWRlbHkgZGlzY3Vzc2VkIG9ubGluZS4gCgoKYGBge3IgZXJuZXN0IGQga3MgdGVzdCByZXN1bHRzfQplcm5lc3RfYnNkc191bmlmb3JtX3Jlc3VsdHMgPSByZWFkLmNzdihoZXJlOjpoZXJlKCdlcm5lc3QtMjAwNS1maWxlcy9lcm5lc3RfYXBwZW5kaXhBLmNzdicpLCBzdHJpbmdzQXNGYWN0b3JzID0gRikgJT4lCiAgZHBseXI6OmxlZnRfam9pbihlcm5lc3Rfa2V5LCBieSA9ICdzaXRlJykKcHJpbnQoZXJuZXN0X2JzZHNfdW5pZm9ybV9yZXN1bHRzKQpgYGAKCgojIyMjIFRyYW5zbGF0aW9uIHRvIGByZXBsaWNhdGUtYmVjc2A6CgoKKkZyb20gWmFyICgxOTk5KSBfQmlvc3RhdGlzdGljYWwgQW5hbHlzaXNfLioKCiMjIyMjIEJhc2UgSy1TIHRlc3QKLSBUYWtlIHZlY3RvciBvZiBtZWFzdXJlbWVudHMgJFhfaSQuIAotIEZvciBlYWNoICRYX2kkIHJlY29yZCB0aGUgb2JzZXJ2ZWQgZnJlcXVlbmN5ICRmX2kkIChudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHdpdGggdGhhdCB2YWx1ZSkuCi0gRGV0ZXJtaW5lIGN1bXVsYXRpdmUgb2JzZXJ2ZWQgZnJlcXVlbmNpZXMgJEZfaSQgYW5kIGN1bXVsYXRpdmUgcmVsYXRpdmUgZnJlcXVlbmNpZXMgJFx0ZXh0cm17cmVsfUZfaSQ6Ci0gJFx0ZXh0cm17cmVsfUZfaSA9IFxmcmFje0ZfaX17bn0kIHdoZXJlICRuJCBpcyB0aGUgbnVtYmVyIG9mIG1lYXN1cmVtZW50cyB0YWtlbi4gCi0gJFx0ZXh0cm17cmVsfUZfaSQgaXMgdGhlIHByb3BvcnRpb24gb2YgdGhlIHNhbXBsZSB0aGF0IGlzIG1lYXN1cmVtZW50cyAkXGxlcSBYX2kkLiAKLSBGb3IgZWFjaCAkWF9pJCwgZGV0ZXJtaW5lIHRoZSBjdW11bGF0aXZlICpyZWxhdGl2ZSogZXhwZWN0ZWQgZnJlcXVlbmN5IGZyb20gdGhlIGNvbXBhcmlzb24gZGlzdHJpYnV0aW9uLCAkXHRleHRybXtyZWx9XGhhdHtGX2l9JC4KLSBGb3IgYSB1bmlmb3JtIGRpc3RyaWJ1dGlvbiwgJFx0ZXh0cm17cmVsfVxoYXR7Rl9pfSA9IFxmcmFje1hfaSAtIFxtaW4oWCl9e1xtYXgoWCkgLSBcbWluKFgpfSQKLSBEZXRlcm1pbmUgJERfaSQgYW5kICREJ19pJCBhczoKLSAkRF9pID0gfHtcdGV4dHJte3JlbH1GX2kgLSBcdGV4dHJte3JlbH1caGF0e0ZfaX19fCQKLSAkRCdfaSA9IHx7XHRleHRybXtyZWx9Rl97aS0xfSAtIFx0ZXh0cm17cmVsfVxoYXR7Rl9pfX18JAotIG5vdGUgJEZfMCA9IDAkIHNvICREJ18xID0gXHRleHRybXtyZWx9XGhhdHtGX2l9JAotIFRoZSB0ZXN0IHN0YXRpc3RpYyAkRCQgaXM6Ci0gJEQgPSBcbWF4WyhcbWF4KERfaSksIChcbWF4KEQnX2kpXSQKLSBDb21wYXJlIHRvIGNyaXRpY2FsIHZhbHVlcyBmcm9tIGFwcGVuZGl4LgoKCiMjIyMjICRcZGVsdGEkLWNvcnJlY3RlZCBLUyB0ZXN0CgotIEZvciBzbWFsbCBzYW1wbGUgc2l6ZXMgKDwyNSkgd2UgY2FuIG9idGFpbiBpbmNyZWFzZWQgcG93ZXIgdXNpbmcgdGhlICRcZGVsdGEkLWNvcnJlY3RlZCBLUyB0ZXN0LgotIEZvciBlYWNoICRpJCBkZXRlcm1pbmUKLSAkXHRleHRybXtyZWx9R19pID0gXGZyYWN7Rl9pfXtuICsgMX0kCi0gJFx0ZXh0cm17cmVsfUcnX2kgPSBcZnJhY3tGX2kgLSAxfXtuIC0gMX0kCi0gVGhlbiBvYnRhaW4gc2ltaWxhciAkRCRzCi0gJERfezAsIGl9ID0gfFx0ZXh0cm17cmVsfUdfaSAtIFx0ZXh0cm17cmVsfVxoYXR7Rl9pfXwkCi0gJERfezEsIGl9ID0gfFx0ZXh0cm17cmVsfUcnX2kgLSBcdGV4dHJte3JlbH1caGF0e0ZfaX18JAotIFRoZSB0ZXN0IHN0YXRpc3RpYyBpcyBlaXRoZXIgJFxtYXgoRF97MCwgaX0pJCBvciAkXG1heChEX3sxLCBpfSkkLCB3aGljaGV2ZXIgbGVhZHMgdG8gdGhlIGhpZ2hlc3QgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlL3NtYWxsZXN0IHByb2JhYmlsaXR5LiBMb29rIHVwIHNpZ25pZmljYW5jZSBpbiB0YWJsZSBmcm9tIGFwcGVuZGl4LiBUaGUgMSBhbmQgMCBhcmUgdGhlICRcZGVsdGEkcy4gCgoKVGFibGVzIG9mIGNyaXRpY2FsIHZhbHVlcyB3ZXJlIGVudGVyZWQgYnkgaGFuZCBmcm9tIHRoZSBhcHBlbmRpeCB0byBaYXIgKDE5OTkpLgoKYGBge3IgYnNkcyBkZWx0YWtzIHRvIHVuaWZvcm0sIGVjaG8gPSBGfQpic2Rfa3NfdGVzdF9yYXcgPSBsYXBwbHkoYnNkcywgRlVOID0gemFyX2tzX3Rlc3QsIGRlbHRhX2NvcnJlY3Rpb24gPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgZm9jYWxfY29sdW1uID0gJ3NwZWNpZXNfbWVhbl9tYXNzJywKICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVjdGVkX3JhbmdlID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgIG5fb3JfaSA9ICduJykKCmJzZF9rc190ZXN0X3Jhd19yZXN1bHRzID0gZGF0YS5mcmFtZSgKICBjb21tdW5pdHlfbmFtZSA9IGFzLmNoYXJhY3RlcihuYW1lcyhic2Rfa3NfdGVzdF9yYXcpKSwKICBzaWduaWYgPSB2YXBwbHkoYnNkX2tzX3Rlc3RfcmF3LCBGVU4gPSBleHRyYWN0X3ZhbHVlc196YXJrcywgdmFsX25hbWUgPSAic2lnbmlmIiwgRlVOLlZBTFVFID0gVFJVRSksCiAgcF9tYXggPSB2YXBwbHkoYnNkX2tzX3Rlc3RfcmF3LCBGVU4gPSBleHRyYWN0X3ZhbHVlc196YXJrcywgdmFsX25hbWUgPSAicF9tYXgiLCBGVU4uVkFMVUUgPSAuMSksIAogIHBfbWluID0gdmFwcGx5KGJzZF9rc190ZXN0X3JhdywgRlVOID0gZXh0cmFjdF92YWx1ZXNfemFya3MsIHZhbF9uYW1lID0gInBfbWluIiwgRlVOLlZBTFVFID0gLjEpLAogIGRfc3RhdGlzdGljID0gdmFwcGx5KGJzZF9rc190ZXN0X3JhdywgRlVOID0gZXh0cmFjdF92YWx1ZXNfemFya3MsIHZhbF9uYW1lID0gImQiLCBGVU4uVkFMVUUgPSAuMSksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEYKKSAlPiUgCiAgZHBseXI6OmxlZnRfam9pbihlcm5lc3RfYnNkc191bmlmb3JtX3Jlc3VsdHMsIGJ5ID0gJ2NvbW11bml0eV9uYW1lJykKCnByaW50KGJzZF9rc190ZXN0X3Jhd19yZXN1bHRzKQoKYGBgCgpUaGUgJFxkZWx0YSQgY29ycmVjdGVkIEtTIHRlc3QgZG9lcyBub3QgY29ycmVzcG9uZCB0byB0aGUgcmVzdWx0cyBmcm9tIEVybmVzdCB3aGVuIHRoZSBzcGVjaWVzIG1lYW4gYm9keSBzaXplIHZhbHVlcyBhcmUgb24gYW4gdW50cmFuc2Zvcm1lZCBzY2FsZS4gCgpVc2luZyB0aGUgbmF0dXJhbCBsb2cgb2YgdGhlIHNwZWNpZXMgbWVhbiBib2R5IHNpemUgdmFsdWUsIGhvd2V2ZXIuLi46CgpgYGB7ciBic2RzIGRlbHRha3MgdG8gdW5pZm9ybSBsb2csIGVjaG8gPUZ9CmJzZF9rc190ZXN0X2xvZyA9IGxhcHBseShic2RzLCBGVU4gPSB6YXJfa3NfdGVzdCwgZGVsdGFfY29ycmVjdGlvbiA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICBmb2NhbF9jb2x1bW4gPSAnbG5fbWFzcycsCiAgICAgICAgICAgICAgICAgICAgICAgICBleHBlY3RlZF9yYW5nZSA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICBuX29yX2kgPSAnbicpCgpic2Rfa3NfdGVzdF9sb2dfcmVzdWx0cyA9IGRhdGEuZnJhbWUoCiAgY29tbXVuaXR5X25hbWUgPSBhcy5jaGFyYWN0ZXIobmFtZXMoYnNkX2tzX3Rlc3RfbG9nKSksCiAgc2lnbmlmID0gdmFwcGx5KGJzZF9rc190ZXN0X2xvZywgRlVOID0gZXh0cmFjdF92YWx1ZXNfemFya3MsIHZhbF9uYW1lID0gInNpZ25pZiIsIEZVTi5WQUxVRSA9IFRSVUUpLAogIHBfbWF4ID0gdmFwcGx5KGJzZF9rc190ZXN0X2xvZywgRlVOID0gZXh0cmFjdF92YWx1ZXNfemFya3MsIHZhbF9uYW1lID0gInBfbWF4IiwgRlVOLlZBTFVFID0gLjEpLCAKICBwX21pbiA9IHZhcHBseShic2Rfa3NfdGVzdF9sb2csIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3phcmtzLCB2YWxfbmFtZSA9ICJwX21pbiIsIEZVTi5WQUxVRSA9IC4xKSwKICBkX3N0YXRpc3RpYyA9IHZhcHBseShic2Rfa3NfdGVzdF9sb2csIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3phcmtzLCB2YWxfbmFtZSA9ICJkIiwgRlVOLlZBTFVFID0gLjEpLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGCikgJT4lIAogIGRwbHlyOjpsZWZ0X2pvaW4oZXJuZXN0X2JzZHNfdW5pZm9ybV9yZXN1bHRzLCBieSA9ICdjb21tdW5pdHlfbmFtZScpCgpwcmludChic2Rfa3NfdGVzdF9sb2dfcmVzdWx0cykKYGBgCgpXaXRoIG1lYW4gbWFzcyBsb2dnZWQsIGFsbCB0aGUgcmVzdWx0cyByZXBsaWNhdGUgcXVhbGl0YXRpdmVseSAoaS5lLiBub3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSB1bmlmb3JtKSBhbmQgTml3b3QsIGZvciB3aGljaCB0aGUgY3VycmVudGx5LWF2YWlsYWJsZSBkYXRhIG1vc3QgY2xvc2VseSBtYXRjaGVzIHRoYXQgcmVwb3J0ZWQgaW4gRXJuZXN0ICgyMDA1KSwgcmVwbGljYXRlcyBhbG1vc3QgZXhhY3RseSBudW1lcmljYWxseS4gCgojIyMgQ29tcGFyaW5nIEJTRHMgYW1vbmcgY29tbXVuaXRpZXMKCiMjIyMgRXJuZXN0IGFwcHJvYWNoCgpFcm5lc3QgKDIwMDUpIHVzZWQgYSB0d28tc2FtcGxlIEtvbG1vZ29yb3YtU21pcm5vdiB0ZXN0IHRvIGNvbXBhcmUgZXZlcnkgcG9zc2libGUgY29tYmluYXRpb24gb2YgY29tbXVuaXR5LWxldmVsIEJTRHMuIAoKYGBge3IgZXJuZXN0IGtzIHR3byBzYW1wbGUgcmVzdWx0cywgZWNobyA9Rn0KYXBwZW5kaXhfYiA9IHRpZHlfYXBwZW5kaXhfYigpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oZXJuZXN0X2tleSwgYnkgPSBjKCdzaXRlX2EnID0gJ3NpdGUnKSkgJT4lCiAgZHBseXI6OnJlbmFtZShjb21tdW5pdHlfYSA9IGNvbW11bml0eV9uYW1lKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKGVybmVzdF9rZXksIGJ5ID0gYygnc2l0ZV9iJyA9ICdzaXRlJykpICU+JQogIGRwbHlyOjpyZW5hbWUoY29tbXVuaXR5X2IgPSBjb21tdW5pdHlfbmFtZSkKCnByaW50KGFwcGVuZGl4X2IpCgpgYGAKCgoKIyMjIyBUcmFuc2xhdGlvbiB0byBgcmVwbGljYXRlLWJlY3NgCgpgYGB7ciBrcyB0d28gc2FtcGxlfQoKIyB1c2Ugc2FtZSBjb21tdW5pdHkgY29tYmluYXRpb25zIGFzIGJlZm9yZQoKYnNkX2Nyb3NzY29tbV9rcyA9IGxhcHBseShjb21tdW5pdHlfY29tYmluYXRpb25zLCBGVU4gPSBrc19ic2QsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxuX21hc3NfdmFscyA9IEYpCgpgYGAKCmBgYHtyIHByaW50IGtzIHR3byBzYW1wbGUsIGVjaG8gPSBGfQpic2RfY3Jvc3Njb21tX3Jlc3VsdHMgPSBkYXRhLmZyYW1lKAogIGNvbW11bml0eV9hID0gdmFwcGx5KGJzZF9jcm9zc2NvbW1fa3MsIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3Rza3MsIHZhbF9uYW1lID0gImNvbW11bml0eV9hIiwgRlVOLlZBTFVFID0gJ3BvcnRhbCcpLAogIGNvbW11bml0eV9iID0gdmFwcGx5KGJzZF9jcm9zc2NvbW1fa3MsIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3Rza3MsIHZhbF9uYW1lID0gImNvbW11bml0eV9iIiwgRlVOLlZBTFVFID0gJ3BvcnRhbCcpLAogIGtzX2QgPSAgdmFwcGx5KGJzZF9jcm9zc2NvbW1fa3MsIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3Rza3MsIHZhbF9uYW1lID0gInN0YXRpc3RpYyIsIEZVTi5WQUxVRSA9IC41KSwKICAgIHBfdmFsdWUgPSAgdmFwcGx5KGJzZF9jcm9zc2NvbW1fa3MsIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3Rza3MsIHZhbF9uYW1lID0gInAudmFsdWUiLCBGVU4uVkFMVUUgPSAuNSksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEYKKQoKCmJzZF9jcm9zc2NvbW1fam9pbmVkID0gYnNkX2Nyb3NzY29tbV9yZXN1bHRzICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oYXBwZW5kaXhfYiwgYnkgPSBjKCdjb21tdW5pdHlfYScsICdjb21tdW5pdHlfYicpKSAlPiUKICBuYS5vbWl0KCkKCmJzZF9jcm9zc2NvbW1fam9pbmVkMiA9IGJzZF9jcm9zc2NvbW1fcmVzdWx0cyAlPiUKICBkcGx5cjo6cmVuYW1lKGNvbW11bml0eV9iID0gY29tbXVuaXR5X2EsIGNvbW11bml0eV9hID0gY29tbXVuaXR5X2IpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oYXBwZW5kaXhfYiwgYnkgPSBjKCdjb21tdW5pdHlfYScsICdjb21tdW5pdHlfYicpKSAlPiUKICBuYS5vbWl0KCkgJT4lCiAgZHBseXI6OmJpbmRfcm93cyhic2RfY3Jvc3Njb21tX2pvaW5lZCkKCnByaW50KGJzZF9jcm9zc2NvbW1fam9pbmVkMikKCmBgYAoKYGBge3IgcGxvdCBic2QgY3Jvc3Njb21tIHB2YWwgaGlzdCwgZWNobyA9RiwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9IApic2RfcHZhbHNfaGlzdG9ncmFtID0gcGxvdF9jcm9zc2NvbW1fa3NfcHZhbHMoYnNkX2Nyb3NzY29tbV9qb2luZWQyKQoKaW52aXNpYmxlKGJzZF9wdmFsc19oaXN0b2dyYW0pCmBgYAoK